local skynet = require "skynet"
local CMD = require "center_proto"
local snax = require "snax"
local cluster = require "cluster"
local buffrw = require "buffrw"
local cjson = require "cjson"
local db = require "dblib"

local NameLen = 64 --用户昵称长度
local FULLMATCH = {}

local matchser = nil
local MatchStage = 0
local MatchConf = nil --赛事配置 
local UserTb = {}
local TableTb = {}
local UserNum = 0
local TableUserNum --每个桌子人数
local LiuJuNum = 0 --流局加赛次数，不超过2
local Msg = {[1] ="初赛阶段即将结束，等待其他玩家完成比赛", [2] = "预赛阶段即将结束，等待其他玩家完成比赛", [3] = "决赛阶段即将结束，等待颁奖"} 
local UserState = {}
local QueryStr = {}

--local ss= os.date("%Y-%m-%d %H:%M:%S", os.time())

function FULLMATCH.InitUserState()
    
    FULLMATCH.MatchId = FULLMATCH.CreateUUID()
    FULLMATCH.StartMatch = nil
    FULLMATCH.EndMatch = nil
    FULLMATCH.SumLiuJu = 0
    FULLMATCH.TableId = ""
    FULLMATCH.CreatTable =  false
    FULLMATCH.GameAddr = nil
    UserState.RegisterSucceed       = 1 --注册成功
    UserState.RegisterOverWait      = 2 --进假桌子等待
    UserState.MatchOverWait         = 3 --该轮打完，等待其他桌子结束
    UserState.InMatchIng            = 4 --在桌子中打牌
    -- if MatchConf["min_man"] == 4 or MatchConf["min_man"] == 8 then 
        -- FULLMATCH.CheckLiuJu = true  -- 是否进行流局检测
    -- else
        -- FULLMATCH.CheckLiuJu = false
    -- end
    FULLMATCH.CheckLiuJu = false
   
end

function FULLMATCH.CreateUUID()

    local template ="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    d = io.open("/dev/urandom", "r"):read(4)
    math.randomseed(os.time() + d:byte(1) + (d:byte(2) * 256) + (d:byte(3) * 65536) + (d:byte(4) * 4294967296))
    return string.gsub(template, "x", function (c)
          local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb)
          return string.format("%x", v)
          end)

end

function QueryStr:AddStr(str)
    if type(str) == "number" and string.sub(self.str,string.len(self.str))=="(" then        
        self.str = self.str..str
	elseif type(str) == "number" then        
        self.str = self.str..","..str
    elseif type(str) == "string" and  string.sub(self.str,string.len(self.str))=="(" then 
        self.str = self.str.."\""..str.."\""
    elseif type(str) == "string" and  string.len(self.str)== 0 or string.sub(self.str,string.len(self.str))=="(" then 
		self.str = self.str..str
	elseif type(str) == "string" and  string.len(self.str) >0 then 
		if str == ")" then
			self.str = self.str..str
		else		
			self.str = self.str..",\""..str.."\""
		end
	end
end

function QueryStr:Clean()
	self.str = ""
end

function FULLMATCH.SortUserByScore()

    local tmp = 0    
    for i=1,#UserTb-1 do  
        for j=1,#UserTb-i do  
            if UserTb[j].Score < UserTb[j+1].Score then  
                tmp = UserTb[j] 
                UserTb[j] = UserTb[j+1]  
                UserTb[j+1] = tmp  
            end  
        end  
    end  
end

function FULLMATCH.Shuffle(data)

    local temp = {}
    for k, v in pairs(data) do 
        table.insert(temp,v)
    end

    local tab = {}
    local index = 1
    math.randomseed(os.time())
    while #temp ~= 0 do
        local n = math.random(0, #temp)
        if temp[n] ~= nil then
            tab[index] = temp[n]
            table.remove(temp, n)
            index = index + 1
        end
    end
    return tab
end


function FULLMATCH.AddUserLog(IntUserid,IntState,IntMsgId,IsRboot,Rank)

    QueryStr:Clean()
    QueryStr:AddStr("call pr_MatchUserLog(")
    QueryStr:AddStr(IntUserid)
    QueryStr:AddStr(MatchConf["match_type"])
    QueryStr:AddStr(MatchConf["match_name"])
    QueryStr:AddStr(MatchConf["pay_type"])
    QueryStr:AddStr(MatchConf["pay_num"])
    QueryStr:AddStr(IntState)
    QueryStr:AddStr(IntMsgId)
    --QueryStr:AddStr(IntAwardNum)
    QueryStr:AddStr(FULLMATCH.MatchId)
    QueryStr:AddStr(IsRboot)
    QueryStr:AddStr(Rank)
    QueryStr:AddStr(")")
    local res1, res2 = db.call(QueryStr.str)
    
end

function FULLMATCH.AddMatchLog()
   
    QueryStr:Clean()
    QueryStr:AddStr("call pr_MatchMatchLog(")
    QueryStr:AddStr(FULLMATCH.MatchId)
    QueryStr:AddStr(MatchConf["match_type"])
    QueryStr:AddStr(MatchConf["match_name"])
    QueryStr:AddStr(MatchConf["pay_type"])
    QueryStr:AddStr(MatchConf["pay_num"])
    QueryStr:AddStr(TableUserNum)
    QueryStr:AddStr(MatchConf["game_type"])
    QueryStr:AddStr(FULLMATCH.SumLiuJu)
    QueryStr:AddStr(FULLMATCH.TableId)
    QueryStr:AddStr(FULLMATCH.StartMatch)
    QueryStr:AddStr(FULLMATCH.EndMatch)
    QueryStr:AddStr(")")
    skynet.error("[fullmatch] AddMatchLog",QueryStr.str)
    db.call(QueryStr.str)
end

function FULLMATCH.FormotGameNickName(nickname,length)
  
    if nickname==nil then
        return ""
    end
    local lengthUTF_8 = #(string.gsub(nickname, "[\128-\191]", ""))
    if lengthUTF_8 <= length then
        return nickname
    else
        local matchStr = "^"
        for var=1, length do
            matchStr = matchStr..".[\128-\191]*"
        end
        local str = string.match(nickname, matchStr)
        return string.format("%s...",str);
    end
end

function FULLMATCH.GetUserForUserTb(userid)
    for k, v in pairs(UserTb) do 
        if v.UserId == userid then 
            return k
        end
    end
    return nil
end

function FULLMATCH.SetUserState(userid, state)

    local index = FULLMATCH.GetUserForUserTb(userid)
    if UserTb[index] then 
        skynet.error("[fullmatch] SetUserState ",userid,state)
        UserTb[index].State = state
        
    end
end


--通知游戏服务器创建比赛所需的桌子
function FULLMATCH.ToGameCreatTable(liu_flag)
    
    FULLMATCH.CreatTable = false --创建桌子还未成功
    local BuffWrite = buffrw.BuffWrite:new()    
    BuffWrite:proto(CMD.Main_match_module,CMD.Sub_CreateMatchTable)
    BuffWrite:int4(skynet.self())
    if liu_flag then  -- 流局加赛，每次局数为1 
        MatchConf["room_config"].jushu = 1
    else   
        MatchConf["room_config"].jushu = MatchConf["match_config"]["graden"][MatchStage]["jushu"]
    end
    MatchConf["room_config"].difen = MatchConf["match_config"]["graden"][MatchStage]["difen"]
    local roomconf = cjson.encode(MatchConf["room_config"])
    skynet.error("[fullmatch] ToGameCreatTable ",roomconf)
    BuffWrite:int2(string.len(roomconf))
    BuffWrite:str(tostring(roomconf),string.len(roomconf))
    BuffWrite:int4(UserNum/TableUserNum) --桌子数
    BuffWrite:int4(UserNum) --人数
    if MatchStage == CMD.Match_ChuSai then
        local TempUserTb = FULLMATCH.Shuffle(UserTb) --打乱顺序
        
        for i = 1 , UserNum do 
            skynet.error("[fullmatch] ToGameCreatTable UserNum UserId MatchStage",UserNum,TempUserTb[i].UserId,MatchStage)
            BuffWrite:int4(TempUserTb[i].UserId)
            BuffWrite:int4(TempUserTb[i].IsRboot)
            --BuffWrite:str(UserTb[i].NikeName, NameLen)
            BuffWrite:int4(TempUserTb[i].Score)
        end
        
   elseif MatchStage == CMD.Match_YuSai or MatchStage == CMD.Match_JueSai then
  
        for i = 1 , UserNum/2 do           
            skynet.error("[fullmatch] ToGameCreatTable UserNum UserId MatchStage ",UserNum,UserTb[i].UserId,UserTb[UserNum-i+1].UserId,MatchStage)
            BuffWrite:int4(UserTb[i].UserId)
            BuffWrite:int4(UserTb[i].IsRboot)
            --BuffWrite:str(UserTb[i].NikeName, NameLen)
            BuffWrite:int4(UserTb[i].Score)
            
            BuffWrite:int4(UserTb[UserNum-i+1].UserId)
            BuffWrite:int4(UserTb[UserNum-i+1].IsRboot)
            --BuffWrite:str(UserTb[UserNum-i+1].NikeName, NameLen)
            BuffWrite:int4(UserTb[UserNum-i+1].Score)
        end      
            --扩展信息      create_table
    end  
    
    matchser.post.create_table(MatchConf["game_type"],FULLMATCH.GameAddr,BuffWrite.buff)
    skynet.timeout(500,function() FULLMATCH.CheckCreatTable() end)
end

function FULLMATCH.CheckCreatTable()
    
    if FULLMATCH.CreatTable then 
        skynet.error("[fullmatch] MatchStage create table succ  ",MatchStage)
    else
        skynet.error("[fullmatch] MatchStage create table fail  ",MatchStage)
        for k, v in pairs(UserTb) do
        
            FULLMATCH.ReturnUserDiomand(v.UserId) --已报名的退钱 CMD.Sub_ModuleToMatch_NotifyMatchPause
            matchser.post.m_send_to_client(v.UserId, CMD.Main_match_module, CMD.Sub_ModuleToMatch_NotifyMatchPause,string.pack("I1",1))        
            skynet.error("[fullmatch] 游戏服务器创建桌子失败 userid: ",v.UserId,"赛赛id: ",MatchConf["match_name"],"游戏名称: ",MatchConf["game_type"] )
            matchser.post.recv_user_addr(v.UserId,MatchConf["match_number"],skynet.self(),0)
            matchser.post.m_send_to_center(v.UserId, 0)
        end
        matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_RunServer,0)
    end

end

--扣去报名费用
function FULLMATCH.DeductUserDiomand(UserId)

    local sqlstr = "call pr_MatchChagre("..UserId..","..MatchConf["pay_type"]..","..-MatchConf["pay_num"]..",@ReturnVal);"
    local res_sql = "select @ReturnVal;"
    local res1, res2 = db.call(sqlstr, res_sql)
    
    if res2[1]['@ReturnVal'] == 1 then 
        return true
    else
        return false
    end
  
end

--返还报名费用
function FULLMATCH.ReturnUserDiomand(UserId)

    local sqlstr = "call pr_MatchChagre("..UserId..","..MatchConf["pay_type"]..","..MatchConf["pay_num"]..",@ReturnVal);"
    local res_sql = "select @ReturnVal;"
    local res1, res2 = db.call(sqlstr, res_sql)
    
    if res2[1]['@ReturnVal'] == 1 then 
        return true
    else
        return false
    end

end

function FULLMATCH.CheckMatchStart()
 
     if UserNum == MatchConf["min_man"] then 
        MatchStage = MatchStage + 1 --比赛阶段
       	matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_Matching,UserNum) --刷新状态
        for index, user in pairs(UserTb) do 
            FULLMATCH.AddUserLog(user.UserId,4,0,user.IsRboot,0)
        end
        FULLMATCH.StartMatch = os.date("%Y-%m-%d %H:%M:%S", os.time())
        FULLMATCH.ToGameCreatTable()
    end
end

function FULLMATCH.SendUserAllUserLiuJu()
    
    local BuffWrite = buffrw.BuffWrite.new()
    for index, user in pairs(UserTb) do 
       -- BuffWrite:clean()
       -- BuffWrite:int4(UserNum)
        matchser.post.m_send_to_client(user.UserId,CMD.Main_match_module,CMD.Sub_ModuleToMatch_SendAllUserLiuJu,string.pack("I1",1))
    end
end

--报名
function FULLMATCH.UserRegister(userid, msg) 

    local BuffRead = buffrw.BuffRead.new(msg)
    BuffRead:int4()
    --local userid = BuffRead:int4()
    local nikename = BuffRead:str(NameLen)
    local BuffWrite = buffrw.BuffWrite.new() 
    local Register = false 
    --skynet.error("FFFFFF ",userid,string.len(nikename),nikename)
    --local stranme=string.sub(nikename,1,string.find(nikename,string.char(0))-1) 
    --skynet.error("GGGGG ",userid,string.len(stranme),stranme)
    BuffWrite:int4(MatchConf["match_number"])
    BuffWrite:int4(1)--人满开赛
    
    if MatchStage == CMD.MatchState_Pauseing  then 
    
        skynet.error("[fullmatch] UserRegister fail MatchState_Pauseing ",userid)
        BuffWrite:int4(2)  
        matchser.post.recv_user_addr(userid,MatchConf["match_number"],skynet.self(),0) 
    elseif UserNum == MatchConf["min_man"] then 
    
        skynet.error("[fullmatch] UserRegister fail UserNum over ",userid)
        BuffWrite:int4(3) 
        matchser.post.recv_user_addr(userid,MatchConf["match_number"],skynet.self(),0) 
    else
        local Index = FULLMATCH.GetUserForUserTb(userid)
        if Index then 
        
            skynet.error("[fullmatch] UserRegister fail already Register ",userid)
            BuffWrite:int4(2)      
            matchser.post.recv_user_addr(userid,MatchConf["match_number"],skynet.self(),0) 
        elseif  FULLMATCH.DeductUserDiomand(userid) then --扣钻成功
        
            Register = true
            table.insert(UserTb,{UserId = userid, NikeName = nikename, Score = 0, CurJu = 0,IsRboot = 0,State = UserState.RegisterSucceed, TableId = 0})
            UserNum = UserNum +1
            BuffWrite:int4(1) --0 钻石不足 1成功 2其他原因失败 
            matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_ReadyMatch,UserNum)
            matchser.post.recv_user_addr(userid,MatchConf["match_number"],skynet.self(),1)
            matchser.post.m_send_to_center(userid, 1)
            --FULLMATCH.SendUserNumToAllUser()
            FULLMATCH.AddUserLog(userid,1,0,0,0)
            skynet.error("[fullmatch] UserRegister succeed ",userid)
        else
        
            skynet.error("[fullmatch] UserRegister fail else reason ",userid)
            BuffWrite:int4(0) --0 钻石不足 1成功 2其他原因失败  
            matchser.post.recv_user_addr(userid,MatchConf["match_number"],skynet.self(),0)            
        end
    end
    
    matchser.post.m_send_to_client(userid,CMD.Main_match_module,CMD.Sub_MatchToModule_PlayerBaoMingResult,BuffWrite.buff)
   
    if Register then --预进入房间
        BuffWrite:clean()
        BuffWrite:int4(MatchConf["match_number"])
        BuffWrite:int4(UserNum)
        BuffWrite:int4(0) --人满比赛用不上该数据
        BuffWrite:int1(MatchConf["icon_config"]["table_tu"]) --人满比赛用不上该数据
        matchser.post.m_send_to_client(userid,CMD.Main_match_module,CMD.Sub_MatchToModule_PreEnterRoomData,BuffWrite.buff)
        FULLMATCH.SetUserState(userid, UserState.RegisterOverWait)
        FULLMATCH.CheckMatchStart()
    end
end

--取消报名
function  FULLMATCH.UserCancelRegister(UserId, msg)
    skynet.error("[fullmatch] UserCancelRegister")
    
    local BuffRead = buffrw.BuffRead.new(msg)
    BuffRead:int4()
    --local UserId = BuffRead:int4()
    local BuffWrite = buffrw.BuffWrite.new()
    skynet.error("[fullmatch] UserCancelRegister  ",UserId)
    BuffWrite:int4(MatchConf["match_number"])
    BuffWrite:int4(UserId)
    
    if MatchStage < 1 and UserNum > 0 and FULLMATCH.ReturnUserDiomand(UserId) then 
        UserNum = UserNum - 1
        local index = FULLMATCH.GetUserForUserTb(UserId)        
        if index then 
            table.remove(UserTb, index)         
            BuffWrite:int1(1)
            skynet.error("[fullmatch] UserCancelRegister succeed ",UserId)
            matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_ReadyMatch,UserNum) --比赛结束
            matchser.post.recv_user_addr(UserId,MatchConf["match_number"],skynet.self(),0)
            matchser.post.m_send_to_center(UserId, 0)
            --FULLMATCH.SendUserNumToAllUser()
            FULLMATCH.AddUserLog(UserId,2,0,0,0)
        else
            skynet.error("[fullmatch] UserCancelRegister fail no find user ",UserId)
            BuffWrite:int1(0)           
        end
    else
        skynet.error("[fullmatch] UserCancelRegister fail unknown reason ",UserId)
        BuffWrite:int1(0)
    end

    matchser.post.m_send_to_client(UserId, CMD.Main_match_module, CMD.Sub_ModuleToMatch_CancelBaoMinBack, BuffWrite.buff)
end

--比赛开始
function FULLMATCH.MatchStart()
    
    local BuffWrite = buffrw.BuffWrite.new() 
    BuffWrite:int4(skynet.self())
    local TablbNum = 0
    for tableid, tableobj in pairs(TableTb) do 
        TablbNum = TablbNum + 1
        BuffWrite:clean()
        BuffWrite:int4(tableid)
        for k, userid in pairs(tableobj.User) do 
            skynet.error("[fullmatch] MatchStart tableid userid: ",tableid, userid)
            FULLMATCH.SendAllNikeName(userid)
            matchser.post.m_send_to_client(userid,CMD.Main_match_module,CMD.Sub_MatchToModule_NotifyMatchStart,BuffWrite.buff)
        end
    end
    
    skynet.sleep(300)
    BuffWrite:clean()
    BuffWrite:proto(CMD.Main_match_module,CMD.Sub_NotifyMatchStart)
    BuffWrite:int4(skynet.self())
    BuffWrite:int4(TablbNum)
    for k, v in pairs(TableTb) do 
        BuffWrite:int4(k)
        matchser.post.m_send_to_game(k,BuffWrite.buff)
    end
    skynet.error("[fullmatch] MatchStart to GameSvr")
    
end

--游戏服务器返回创建桌子结构
function CMD.recv_create_table(msg)

    FULLMATCH.CreatTable = true --创建桌子有返回
    local BuffRead = buffrw.BuffRead.new(msg)  
    BuffRead:proto()
    BuffRead:int4()
    local result = BuffRead:int4()
    skynet.error("[fullmatch] recv_create_table result ",result)
    if result == 1 then 
        local TableCount =  BuffRead:int4()
        for i = 1, TableCount do
            local Tableid = BuffRead:int4()
            FULLMATCH.GameAddr = math.floor(Tableid / 10000)
            FULLMATCH.TableId = FULLMATCH.TableId..tostring(Tableid)..","
            skynet.error("[fullmatch] recv_create_table tableid: ",Tableid,TableCount)
            TableTb[Tableid] = {User = {}, CurJu = 0 ,LiuJu = 1}
            for j = 1, TableUserNum do 
                local userid = BuffRead:int4()
                skynet.error("[fullmatch] recv_create_table userid: ",userid)     
                table.insert(TableTb[Tableid].User, userid)
                local index = FULLMATCH.GetUserForUserTb(userid)
                if index then
                    UserTb[index].State = UserState.InMatchIng
                    UserTb[index].TableId = Tableid
                    if LiuJuNum == 0 then 
                        UserTb[index].CurJu = 0 
                    end
                else                   
                    table.insert(UserTb,{UserId = userid, Score = 0, CurJu = 0, IsRboot = 1,
                                State = UserState.InMatchIng, TableId = Tableid})
                    UserNum = UserNum + 1
                        
                end
            end
        end
        local RobotNum = BuffRead:int4()
        for i = 1, RobotNum do 
            local RobotId = BuffRead:int4()
            local RobotName = BuffRead:str(NameLen)
            local index = FULLMATCH.GetUserForUserTb(RobotId)
            if index then
                UserTb[index].NikeName = RobotName
            else
                skynet.error("[fullmatch] recv_create_table robot error")
            end
        end
        --通知游戏服务器和客户端开始比赛 
        FULLMATCH.MatchStart()
    else
        for k, v in pairs(UserTb) do
        
            FULLMATCH.ReturnUserDiomand(v.UserId) --已报名的退钱 CMD.Sub_ModuleToMatch_NotifyMatchPause
            matchser.post.m_send_to_client(v.UserId, CMD.Main_match_module, CMD.Sub_ModuleToMatch_NotifyMatchPause,string.pack("I1",1))        
            skynet.error("[fullmatch] 游戏服务器创建桌子失败 userid: ",v.UserId,"赛赛id: ",MatchConf["match_name"],"游戏名称: ",MatchConf["game_type"] )
            matchser.post.recv_user_addr(v.UserId,MatchConf["match_number"],skynet.self(),0)
            matchser.post.m_send_to_center(v.UserId, 0)
        end
        matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_RunServer,0)
    end
  
    
end

function FULLMATCH.SendSortToAllUser()
    
    local BuffWrite = buffrw.BuffWrite.new()
    for index, user in pairs(UserTb) do 
        BuffWrite:clean()
        BuffWrite:int4(index)
        BuffWrite:int4(UserNum)
        matchser.post.m_send_to_client(user.UserId,CMD.Main_match_module,CMD.Sub_ModuleToMatch_RankChangeBack,BuffWrite.buff)
        
    end
end

--检测该轮所有的桌子都结束打牌
function FULLMATCH.CheckAllTableOver()
    
    local AllOver = true
    
    for k, v in pairs(TableTb) do
        if v.CurJu ~= MatchConf["match_config"]["graden"][MatchStage]["jushu"] and not FULLMATCH.CheckLiuJu then        
            AllOver = false
        elseif FULLMATCH.CheckLiuJu  and v.CurJu ~=  1 then 
            AllOver = false
        end   
    end
      
    FULLMATCH.SortUserByScore()
    FULLMATCH.SendSortToAllUser() --排行榜有变化，及时通知客户端
    local BuffWrite = buffrw.BuffWrite.new()
    if AllOver then      
        local OutNum =MatchConf["match_config"]["graden"][MatchStage]["men"] --预计淘汰名次
        if MatchStage < MatchConf["match_config"]["grader_num"] then 
            for i =1 , #UserTb do --排名后面的人淘汰
                BuffWrite:clean()
                BuffWrite:int4(i)               
                if i < OutNum + 1  then
                    UserTb[i].CurJu = 0
                    matchser.post.m_send_to_client(UserTb[i].UserId,CMD.Main_match_module,CMD.Sub_MatchToModule_RoundOverJinJiData,BuffWrite.buff)     
                    skynet.error("[fullmatch] CheckAllTableOver in  ",UserTb[i].UserId,UserTb[i].Score)                      
                else
                    BuffWrite:int8(0)
                    BuffWrite:str(MatchConf["match_name"],32)
                    matchser.post.m_send_to_client(UserTb[OutNum + 1].UserId,CMD.Main_match_module,CMD.Sub_MatchToModule_BiSaiOverResult,BuffWrite.buff)
                    local nAwardId = FULLMATCH.CreatAwardMsg(UserTb[OutNum + 1].UserId,
                                                    UserTb[OutNum + 1].NikeName,
                                                    MatchConf["match_name"],
                                                    i,
                                                    0,
                                                    0 
                                                    )
                    skynet.error("[fullmatch] CheckAllTableOver out ",UserTb[OutNum + 1].UserId,UserTb[OutNum + 1].Score) 
                    FULLMATCH.AddUserLog(UserTb[OutNum + 1].UserId,5,0,UserTb[OutNum + 1].IsRboot,i)
                    matchser.post.m_send_to_center(UserTb[OutNum + 1].UserId, 0)
                    matchser.post.recv_user_addr(UserTb[OutNum + 1].UserId,MatchConf["match_number"],skynet.self(),0)
                    
                    table.remove(UserTb,OutNum + 1)
                    UserNum = UserNum -1
                end            
            end
            MatchStage = MatchStage + 1 --比赛阶段
            for k, v in pairs(TableTb) do 
                TableTb[k] = nil
            end
            skynet.sleep(1000)
            FULLMATCH.ToGameCreatTable()
            
        elseif MatchStage >= MatchConf["match_config"]["grader_num"] then --最后一轮打完
   
            local IsLiu = true             
            for k, v in pairs(TableTb) do 
                if v.LiuJu == 0 then
                    IsLiu = false
                end
            end
            if IsLiu and LiuJuNum <2 and (MatchConf["min_man"] == 4 or MatchConf["min_man"] == 8) then
                skynet.error("[fullmatch] CheckAllTableOver match liu ju LiuJuNum ",LiuJuNum)
                skynet.sleep(1000)
                FULLMATCH.CheckLiuJu = true
                for k, v in pairs(TableTb) do 
                    TableTb[k] = nil
                end
                LiuJuNum = LiuJuNum +1
                MatchConf["match_config"]["graden"][MatchStage]["jushu"] = MatchConf["match_config"]["graden"][MatchStage]["jushu"] + 1
                FULLMATCH.SendUserAllUserLiuJu() --流局通知
                FULLMATCH.ToGameCreatTable(true)
            else
                FULLMATCH.DealAward()
                --**************************检测状态比赛是否失效**************************************--
                if skynet.time() < MatchConf["start_date"] or  skynet.time() > MatchConf["end_date"] then 
                    matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_RunServer,UserNum) 
                end
                
                 --todo 向数据库报告分数等，日志
                FULLMATCH.EndMatch = os.date("%Y-%m-%d %H:%M:%S", os.time())
                FULLMATCH.AddMatchLog()
                BuffWrite:clean()
                BuffWrite:proto(CMD.Main_match_module,CMD.Sub_NotifyMatchOver)
                BuffWrite:int4(skynet.self())
                matchser.post.m_send_to_game(FULLMATCH.GameAddr*10000,BuffWrite.buff)
                matchser.post.end_single_matchser(MatchConf["match_number"], skynet.self())
                  
            end
           
        end
    else
        local WaitNum = 0
        for k, v in pairs(TableTb) do --统计未打完的桌子
            if v.CurJu ~= MatchConf["match_config"]["graden"][MatchStage]["jushu"]  or (LiuJuNum > 0 and v.CurJu == 0) then               
                WaitNum = WaitNum + 1
                skynet.error("[fullmatch] CheckAllTableOver the table not over tableid ",k)
            end
        end
        for k, v in pairs(TableTb) do 
            if v.CurJu == MatchConf["match_config"]["graden"][MatchStage]["jushu"]  or (LiuJuNum > 0 and v.CurJu == 1) then               
                for index, userid in pairs(v.User) do 
                    FULLMATCH.SetUserState(userid, UserState.MatchOverWait)
                    BuffWrite:clean()
                    BuffWrite:int4(WaitNum)   
                    matchser.post.m_send_to_client(userid,CMD.Main_match_module,CMD.Sub_MatchToModule_RoundOverData,BuffWrite.buff)
                    local i = FULLMATCH.GetUserForUserTb(userid)
                    skynet.error("[fullmatch] CheckAllTableOver has must wait ",userid,WaitNum)
                end
            end
        end
    end
        
end

-- 数据库生成消息
function FULLMATCH.CreatAwardMsg(userid, nikename, matchname, pos, prize_type, num)
    
    local title =  matchname.." 第"..pos.."名"
    local body
    local msgtype = 201
    local strname = string.sub(nikename,1,string.find(nikename,string.char(0))-1) 
    skynet.error("[fullmatch] CreatAwardMsg",userid, strname, matchname, pos, prize_type, num)
    if prize_type > 0 then
        local goods = nil
        if prize_type == 1 then 
            goods = "钻石奖励。"
            msgtype = 201  --领取 钻石 老友的消息类型，该类型不需要输入电话号码
        elseif prize_type == 2 then 
            goods = "老友豆奖励。"
            msgtype = 201
        elseif prize_type == 3 then 
            goods = "元话费奖励。"
            msgtype = 202   --领取 话费的消息类型，该类型需要输入电话号码
        end
        
        body = strname.."力战群雄，在“"..matchname.."”中获得第"..pos.."名，赢得"..num..goods
    else
        body = "真遗憾，"..strname.."在“"..matchname.."”中获得第"..pos.."名，差一点点就能拿到大奖了。"   
    end
    skynet.error("[function] CreatAwardMsg ",body)
    local sqlstr = "call pr_AddMatchAwardMsg("..userid..","..msgtype..",\""..title.."\",\""..body.."\","..prize_type..","..num..",@ReturnVal,@MsgId);"
    local res_sql = "select @ReturnVal,@MsgId;"
    local res1, res2 = db.call(sqlstr, res_sql)
    
    if res2[1]['@ReturnVal'] == 1 then 
        return res2[1]['@MsgId'] 
    else
        return 0
    end

end

--该场赛事结束后颁奖处理
function FULLMATCH.DealAward()
    
    local BuffWrite = buffrw.BuffWrite.new()
    
    for i = 1, MatchConf["prize_config"]["prize_num"] do --有奖励的玩家处理               
        for Start = MatchConf["prize_config"]["prize"][i]["start_level"], MatchConf["prize_config"]["prize"][i]["end_level"] do 
            BuffWrite:clean()
            BuffWrite:int4(Start)
            local nAwardId = FULLMATCH.CreatAwardMsg(UserTb[Start].UserId,
                                                    UserTb[Start].NikeName,
                                                    MatchConf["match_name"],
                                                    Start,
                                                    tonumber(MatchConf["prize_config"]["prize"][i]["prize_type"]) ,
                                                    tonumber(MatchConf["prize_config"]["prize"][i]["num"])  
                                                    )
            BuffWrite:int8(nAwardId)
            BuffWrite:int4(MatchConf["prize_config"]["prize"][i]["prize_type"]) 
            BuffWrite:int4(MatchConf["prize_config"]["prize"][i]["num"])
            skynet.error("[fullmatch] CheckAllTableOver match over ",UserTb[Start].UserId,nAwardId) 
            FULLMATCH.AddUserLog(UserTb[Start].UserId, 5, nAwardId , UserTb[Start].IsRboot,Start)
            matchser.post.m_send_to_client(UserTb[Start].UserId,CMD.Main_match_module,CMD.Sub_MatchToModule_BiSaiOverResult,BuffWrite.buff)
            matchser.post.m_send_to_center(UserTb[Start].UserId, 0)
            matchser.post.recv_user_addr(UserTb[Start].UserId,MatchConf["match_number"],skynet.self(),0)
        end
    end
    
    for i= MatchConf["win_man"]+1 , #UserTb do  --没有奖励的玩家处理
        BuffWrite:clean()
        BuffWrite:int4(i)
        local nAwardId = FULLMATCH.CreatAwardMsg(UserTb[i].UserId,
                                                UserTb[i].NikeName,
                                                MatchConf["match_name"],
                                                i,
                                                0,
                                                0
                                                )
        BuffWrite:int8(0)
        BuffWrite:int4(0) 
        BuffWrite:int4(0)
        skynet.error("[fullmatch] CheckAllTableOver match over fail ",UserTb[i].UserId,nAwardId) 
        FULLMATCH.AddUserLog(UserTb[i].UserId,5,0,UserTb[i].IsRboot,i)
        matchser.post.m_send_to_client(UserTb[i].UserId,CMD.Main_match_module,CMD.Sub_MatchToModule_BiSaiOverResult,BuffWrite.buff)
        matchser.post.m_send_to_center(UserTb[i].UserId, 0)
        matchser.post.recv_user_addr(UserTb[i].UserId,MatchConf["match_number"],skynet.self(),0)
    end
    
end

--获得比赛的详细信息
function  FULLMATCH.GetMatchInfo(msg)

    -- BuffRead = buffrw.BuffRead.new(msg)
    -- local UserId = BuffRead:int4()
    -- local BuffWrite = buffrw.BuffWrite.new() 
    -- BuffWrite:int4(MatchConf["match_number"]) --赛事编号 
    -- BuffWrite:int4(MatchConf["game_type"])   --赛事玩法
    -- BuffWrite:int4(MatchConf["min_man"])  --最小人数
    -- BuffWrite:int1(MatchConf["icon_config"]["bao_min"])   --赛制类型（赛制图片地址）
    -- BuffWrite:int1(MatchConf["icon_config"]["hall_prize_icon"])   --奖励图片地址
    -- BuffWrite:int1(MatchConf["icon_config"]["baomin_name"])   --标题图片地址 
    -- matchser.post.m_send_to_client(UserId, CMD.Main_match_module, CMD.Sub_MatchToModule_SingleBiSaiBack, BuffWrite.buff)
end

--收到一桌结算消息
function FULLMATCH.RevTableResult(msg)

    local BuffRead = buffrw.BuffRead.new(msg)  
    BuffRead:proto()
    BuffRead:int4()
    local TableId = BuffRead:int4()
    local UserCount = BuffRead:int4()
    local IsLiu = BuffRead:int4()
    if IsLiu == 0 then 
        TableTb[TableId].LiuJu = 0
    elseif  IsLiu == 1 then 
        FULLMATCH.SumLiuJu = FULLMATCH.SumLiuJu + 1
    end
    skynet.error("[fullmatch] RevTableResult tableid, usernum IsLiu: ",TableId,UserCount,IsLiu)
    TableTb[TableId].CurJu = TableTb[TableId].CurJu + 1
    local UserId, Score, k
    for i = 1, UserCount do 
        UserId = BuffRead:int4()
        Score = BuffRead:int4()
        skynet.error("[fullmatch] RevTableResult userid,score: ",UserId,Score)
        k = FULLMATCH.GetUserForUserTb(UserId)
        if k then 
           UserTb[k].Score = UserTb[k].Score + Score
           UserTb[k].CurJu = UserTb[k].CurJu + 1
           skynet.error("[fullmatch] RevTableResult userid,score curju: ",UserId,UserTb[k].Score,UserTb[k].CurJu)
        end
    end
    
    FULLMATCH.CheckAllTableOver()
    
end

function FULLMATCH.GetRoomInfo(userid, msg)

    local BuffWrite = buffrw.BuffWrite.new()  
    BuffWrite:int4(MatchStage)                              --当前轮数
    BuffWrite:int4(MatchConf["match_config"]["grader_num"]) --最大轮数
    BuffWrite:int4(FULLMATCH.GetUserForUserTb(userid))      --排名
    BuffWrite:int4(#UserTb)                                 --总人数
    
    matchser.post.m_send_to_client(userid, CMD.Main_match_module, CMD.Sub_MatchToModule_BiSaiRoomInfo, BuffWrite.buff)
end

--help消息请求
function FULLMATCH.BiSaiHelp(userid, msg)
    local BuffWrite = buffrw.BuffWrite.new()
    skynet.error("[FULLMATCH] FULLMATCH.BiSaiHelp MatchConf[icon_config][help] " ,MatchConf["icon_config"]["help"])
    BuffWrite:int4(MatchConf["icon_config"]["help"])   
    matchser.post.m_send_to_client(userid, CMD.Main_match_module, CMD.Sub_ModuleToMatch_BiSaiHelpInfoBack, BuffWrite.buff)
end

--客户端定时获取排行榜数据
function FULLMATCH.GetMatchRankData(userid, msg)
    
    local BuffWrite = buffrw.BuffWrite.new()
    BuffWrite:int4(#UserTb)
    skynet.error("[fullmatch] GetMatchRankData #UserTb UserNum ",#UserTb,UserNum)
    for k, v in ipairs(UserTb) do 
        BuffWrite:int4(v.UserId)
       -- skynet.error("[fullmatch] GetMatchRankData",v.UserId)
        BuffWrite:int4(k)
        BuffWrite:int4(v.Score)
        --if v.CurJu >= MatchConf["match_config"]["graden"][MatchStage]["jushu"] then 
        --    BuffWrite:int4(MatchConf["match_config"]["graden"][MatchStage]["jushu"])
       -- else
            BuffWrite:int4(v.CurJu)
       --end
        BuffWrite:int4(MatchConf["match_config"]["graden"][MatchStage]["jushu"])
    end
    
    matchser.post.m_send_to_client(userid, CMD.Main_match_module, CMD.Sub_MatchToModule_BiSaiRankDataBack, BuffWrite.buff)

end

--发送全部用户的昵称到客户端
function FULLMATCH.SendAllNikeName(userid)
    
    local BuffWrite = buffrw.BuffWrite.new()
    BuffWrite:int4(#UserTb)
    for k, v in ipairs(UserTb) do 
        BuffWrite:int4(v.UserId) 
        local ok, nickname  = pcall(FULLMATCH.FormotGameNickName, v.NikeName,5)
        if ok then
            if string.len(nickname) > 24 then
                skynet.error("[fullmatch] error 按照字数截取长度失败 ",string.len(nickname),nickname)
                nickname = string.sub(nickname,1,24)
            end
            BuffWrite:str(nickname, 24)
        else
            BuffWrite:str(" ", 24)
        end
    end  
    matchser.post.m_send_to_client(userid, CMD.Main_match_module, CMD.Sub_ModuleToMatch_SendAllUserNikeName, BuffWrite.buff)

end

--断线重连数据下发
function FULLMATCH.MatchOffBackData(userid, msg)
    
    FULLMATCH.SendAllNikeName(userid)
    local BuffWrite = buffrw.BuffWrite.new()
    local index = FULLMATCH.GetUserForUserTb(userid)
    BuffWrite:int4(MatchStage)
    skynet.error("[fullmatch] MatchOffBackData userid  state ",userid, UserTb[index].State)
    if index then 
        BuffWrite:int4(UserTb[index].State)
        BuffWrite:int4(UserTb[index].Score)       
        --if MatchStage >0 and UserTb[index].CurJu >= MatchConf["match_config"]["graden"][MatchStage]["jushu"] then 
       --     BuffWrite:int4(MatchConf["match_config"]["graden"][MatchStage]["jushu"])
       -- else
            BuffWrite:int4(UserTb[index].CurJu)
       -- end
    else
        BuffWrite:int4(0)
        BuffWrite:int4(0)
        BuffWrite:int4(0)
    end
    if MatchStage >0 then    
        BuffWrite:int4(MatchConf["match_config"]["graden"][MatchStage]["jushu"])
    else
        BuffWrite:int4(0)
    end
    BuffWrite:int4(MatchConf["match_number"])
    BuffWrite:int4(UserNum)
    BuffWrite:int4(0) 
    BuffWrite:int1(MatchConf["icon_config"]["table_tu"])   --牌桌底图 

    matchser.post.m_send_to_client(userid, CMD.Main_match_module, CMD.Sub_ModuleToMatch_BiSaiOffBackMatch, BuffWrite.buff)
end


function FULLMATCH.MapMatchSer()

    matchser = cluster.snax("center", "matchser")
    if matchser then 
        skynet.error("[fullmatch] matchser server map succeed")
    else
        skynet.error("[fullmatch] matchser server map fail")
    end
   
  -- FULLMATCH.DeductUserDiomand(46296)
end



function FULLMATCH.PublishMatch() --发布赛事
   
    MatchStage = 0 
    skynet.error("[fullmatch] PublishMatch")
    if skynet.time() > MatchConf["start_date"] and  skynet.time() < MatchConf["end_date"] then 
        matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_ReadyMatch,UserNum) 
    end
    
end

function FULLMATCH.PauseMatch() --暂停赛事
    
    skynet.error("[fullmatch] PauseMatch")
    
    if MatchStage < CMD.Match_ChuSai then  -- 比赛还未开始
        MatchStage = CMD.MatchState_Pauseing 
        for k, v in pairs(UserTb) do
            FULLMATCH.ReturnUserDiomand(v.UserId) --已报名的退钱 CMD.Sub_ModuleToMatch_NotifyMatchPause
            matchser.post.m_send_to_client(v.UserId, CMD.Main_match_module, CMD.Sub_ModuleToMatch_NotifyMatchPause,string.pack("I1",1))
            matchser.post.recv_user_addr(v.UserId,MatchConf["match_number"],skynet.self(),0)
            matchser.post.m_send_to_center(v.UserId, 0)
            skynet.error("[fullmatch] PauseMatch",v.UserId)
        end
    end
    matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_Pauseing,UserNum)
end

function FULLMATCH.CancelPublishMatch() --取消发布赛事
    
    skynet.error("[fullmatch] CancelPublishMatch")    
    if MatchStage < CMD.Match_ChuSai then  -- 比赛还未开始
        MatchStage = CMD.MatchState_Pauseing
        for k, v in pairs(UserTb) do
            FULLMATCH.ReturnUserDiomand(v.UserId) --已报名的退钱 CMD.Sub_ModuleToMatch_NotifyMatchPause
            matchser.post.m_send_to_client(v.UserId, CMD.Main_match_module, CMD.Sub_ModuleToMatch_NotifyMatchPause,string.pack("I1",1))
            matchser.post.recv_user_addr(v.UserId,MatchConf["match_number"],skynet.self(),0)
            matchser.post.m_send_to_center(v.UserId, 0)
            skynet.error("[fullmatch] CancelPublishMatch",v.UserId)    
        end
    end
    matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_RunServer,UserNum) 
end

function FULLMATCH.OffLineMatch() -- 下线赛事
   
    skynet.error("[fullmatch] OffLineMatch")
    for k, v in pairs(UserTb) do
        skynet.error("[fullmatch] OffLineMatch",v.UserId)    
    end
    matchser.post.recv_match_state(MatchConf["match_number"],CMD.MatchState_Error,UserNum) 

end

function FULLMATCH.CheckHeart()
    
    -- while true do 
        
        -- matchser.post.get_game_refresh_time
    -- end
end

function CMD.recv_client(userid,scmd,msg)--mcmd,scmd,msg

    if CMD.Sub_ModuleToMatch_PlayerBaoMingReq == scmd then --报名
    
        FULLMATCH.UserRegister(userid,msg)
        
    elseif CMD.Sub_ModuleToMatch_GetSingleBiSaiReq == scmd then 
    
        FULLMATCH.GetMatchInfo(userid,msg) 
        
    elseif CMD.Sub_ModuleToMatch_CancelBaoMinReq == scmd then 
    
        FULLMATCH.UserCancelRegister(userid,msg)
        
    elseif CMD.Sub_ModuleToMatch_GetBiSaiRoomInfoReq == scmd then 
    
        FULLMATCH.GetRoomInfo(userid, msg)
        
    elseif CMD.Sub_ModuleToMatch_BiSaiRankDataReq == scmd then 
    
        FULLMATCH.GetMatchRankData(userid, msg)
    
    elseif CMD.Sub_ModuleToMatch_BiSaiOffBack == scmd then
        
        FULLMATCH.MatchOffBackData(userid, msg)
        
    elseif CMD.Sub_ModuleToMatch_GetBiSaiHelpInfo == scmd then
    
        FULLMATCH.BiSaiHelp(userid, msg)    
    end   
end

function CMD.recv_game(scmd,msg)
    if CMD.Sub_RecvTableScore == scmd  then
    
       FULLMATCH.RevTableResult(msg) 
       
    end
end


function CMD.contrl(active_id)

    if active_id ==  CMD.MacthContrl_pause then 
    
        FULLMATCH.PauseMatch()
        
    elseif active_id == CMD.MacthContrl_publish then 
    
        FULLMATCH.PublishMatch() 
        
    elseif active_id == CMD.MacthContrl_not_publish then 
    
        FULLMATCH.CancelPublishMatch()
        
    elseif active_id == CMD.MacthContrl_not_online then
    
        FULLMATCH.OffLineMatch()
    end
end



function CMD.exit()

    skynet.error("[fullmatch] ser exit ",skynet.self())
    skynet.exit()
    
end


function FULLMATCH.test()

    for k =10, 522 do 
        local playid = 44100+k
        local msg = string.pack("<i4<i4c64",25,playid,"哈哈地方") 
        FULLMATCH.UserRegister(msg)
    end

end



function CMD.start(conf) 
    
    skynet.error("[fullmatch] start ")  
    MatchConf = conf   
    TableUserNum = MatchConf["room_config"]["player_num"]
    MatchStage = 0 
    FULLMATCH.InitUserState() 
    FULLMATCH.MapMatchSer()
    skynet.fork(FULLMATCH.CheckHeart)
   -- skynet.timeout(2000, function() FULLMATCH.test() end) 
end


skynet.start(function()
    skynet.dispatch("lua", function(session, source, cmd, subcmd, ...)
		local f = assert(CMD[cmd])
		skynet.ret(skynet.pack(f(subcmd, ...)))
	end)
    skynet.error("[fullmatch] start...")
    math.randomseed(os.time())
end)
